﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AAA.Chart.Data;
using AAA.Meta.Chart;
using AAA.Chart.Indicator;
using System.Drawing.Drawing2D;
using AAA.Meta.Quote;
using AAA.Meta.Chart.Data;

namespace AAA.Chart.Component
{
    public partial class ChartBase : UserControl, IChartMeta
    {
        public event MoueClickEvent OnMouseClick;
        public event MoueDoubleClickEvent OnMouseDoubleClick;
        public event MoueEnterEvent OnMouseEnter;
        public event MoueLeaveEvent OnMouseLeave;
        public event MoueMoveEvent OnMouseMove;
        public event MoueDownEvent OnMouseDown;
        public event MoueUpEvent OnMouseUp;

        // Pad Setting
        private int _iLeftPad = ChartConstants.DEFAULT_PAD * 0;
        private int _iTopPad = ChartConstants.DEFAULT_PAD * 0;
        private int _iRightPad = ChartConstants.DEFAULT_PAD * 0;
        private int _iBottomPad = ChartConstants.DEFAULT_PAD;
        protected int _iAxisWidth = ChartConstants.DEFAULT_AXIS_WIDTH;
        protected int _iTitleHeight = ChartConstants.DEFAULT_TITLE_HEIGHT;

        // Chart Meta Data
        protected double[] _dMin = new double[4];
        protected double[] _dMax = new double[4];

        private string _strChartName;
        private IDataSource _dsDataSource;
        private List<IIndicator> _lstIndicator;
        private Color _cBorderColor = Color.White;
        private Color _cAxisLineColor = Color.White;
        private Color _cHourLineColor = Color.White;
        private Color _cDayLineColor = Color.White;
        private bool _isShowXAxis = true;
        private bool _isShowYAxis = true;
        private bool _isShowHourLine = false;
        private bool _isShowDayLine = false;
        protected double _dPixelValue;
        protected double _dMinValue;
        protected double _dX2PixelValue;
        protected double _dX2MinValue;
        private int _iXScale;
        private List<ConstantLine> _lstCustomLine;

        private ScaleTypeEnum _eXScaleType = ScaleTypeEnum.Screen;
        private ScaleTypeEnum _eYScaleType = ScaleTypeEnum.Screen;

        protected float _fXOrigin;
        protected float _fYOrigin;

        protected float _fX2Origin;
        protected float _fY2Origin;

        private int _iYAxisPosition = ChartConstants.LEFT;
        private int _iXDataType = ChartConstants.DATA_TYPE_TIME;
        private int _iStartIndex;
        private int _iEndIndex;
        //        private int _iBarCount = -1;
        private double _dXEdgeRatio = 0;
        private double _dYEdgeRatio = 0;
        protected long _lLastUpdateTicks = 0;
        private int _iUpdateFreq = 0;

        public ChartBase()
        {
            InitializeComponent();

            BackColor = Color.Black;
            _lstIndicator = new List<IIndicator>();
            _lstCustomLine = new List<ConstantLine>();
            _iXScale = ChartConstants.DEFAULT_X_SCALE;            
            CalculateOrigin();
            Paint += new PaintEventHandler(Chart_Paint);
        }

        protected virtual void CalculateOrigin()
        {
            _fXOrigin = LeftPad + ((_iYAxisPosition == ChartConstants.LEFT) && ShowYAxis ? _iAxisWidth : 0);
            _fYOrigin = DrawHeight;
        }
/*
        protected override void OnPaint(PaintEventArgs e)        
        {
            //e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);            
            PaintChart();
            base.OnPaint(e);            
        }
*/
        void Chart_Resize(object sender, EventArgs e)
        {
            try
            {
                //                _iBarCount = (int)Math.Floor((double)DrawWidth / (double)(XScale * ChartConstants.MIN_X_INTERVAL));
                PaintChart();
                this.Invalidate();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "," + ex.StackTrace);
            }
        }

        void Chart_Validated(object sender, EventArgs e)
        {
            PaintChart();
            this.Invalidate();
        }

        void Chart_Paint(object sender, PaintEventArgs e)
        {
            PaintChart();
        }

        void Chart_VisibleChanged(object sender, EventArgs e)
        {
            PaintChart();
            this.Invalidate();
        }


        public int YAxisPosition
        {
            get { return _iYAxisPosition; }
            set { _iYAxisPosition = value; }
        }


        public bool IsShowXAxis
        {
            get { return _isShowXAxis; }
            set { _isShowXAxis = value; }
        }

        public bool IsShowYAxis
        {
            get { return _isShowYAxis; }
            set { _isShowYAxis = value; }
        }

        public bool IsShowHourLine
        {
            get { return _isShowHourLine; }
            set { _isShowHourLine = value; }
        }

/*
        public bool IsShowDayLine1
        {
            get { return _isShowDayLine; }
            set { _isShowDayLine = value; }
        }
*/
        public IDataSource DataSource
        {
            get { return _dsDataSource; }
            set { _dsDataSource = value; }
        }

        public Color HourLineColor
        {
            get { return _cHourLineColor; }
            set { _cHourLineColor = value; }
        }

        public Color DayLineColor
        {
            get { return _cDayLineColor; }
            set { _cDayLineColor = value; }
        }

        public ScaleTypeEnum XScaleType
        {
            get { return _eXScaleType; }
            set { _eXScaleType = value; }
        }

        public ScaleTypeEnum YScaleType
        {
            get { return _eYScaleType; }
            set { _eYScaleType = value; }
        }

        public int UpdateFreq
        {
            get { return _iUpdateFreq; }
            set { _iUpdateFreq = value; }
        }

        public double XEdgeRatio
        {
            get { return _dXEdgeRatio; }
            set { _dXEdgeRatio = value; }
        }

        public double YEdgeRatio
        {
            get { return _dYEdgeRatio; }
            set { _dYEdgeRatio = value; }
        }

        public int XDataType
        {
            get { return _iXDataType; }
            set { _iXDataType = value; }
        }

        public int StartIndex
        {
            get { return _iStartIndex; }
            set
            {
                _iStartIndex = value;
                foreach (IIndicator indicator in _lstIndicator)
                    indicator.StartIndex = value;
            }
        }

        public int EndIndex
        {
            get { return _iEndIndex; }
            set
            {
                _iEndIndex = value;
                foreach (IIndicator indicator in _lstIndicator)
                    indicator.EndIndex = value;

            }
        }

        public int XScale
        {
            get { return _iXScale; }
            set
            {
                _iXScale = Math.Max(1, value);
                //                    _iBarCount = (int)Math.Floor((double)DrawWidth / (double)(_iXScale * ChartConstants.MIN_X_INTERVAL));
            }
        }

        public string ChartName
        {
            get { return _strChartName; }
            set { _strChartName = value; }
        }

        public void Clear()
        {

        }

        public void AddCustomLine(ConstantLine customLine)
        {
            if (_lstCustomLine.Contains(customLine) == false)
                _lstCustomLine.Add(customLine);
        }

        public void ShowHourLine(bool isShowHourLine, Color cHourLineColor)
        {
            _isShowHourLine = isShowHourLine;
            _cHourLineColor = cHourLineColor;
        }

        public void ShowDayLine(bool isShowDayLine, Color cDayLineColor)
        {
            IsShowDayLine = isShowDayLine;
            _cDayLineColor = cDayLineColor;
        }

        public int LeftPad
        {
            get { return _iLeftPad; }
            set { _iLeftPad = value; }
        }

        public int TopPad
        {
            get { return _iTopPad; }
            set { _iTopPad = value; }
        }

        public int RightPad
        {
            get { return _iRightPad; }
            set { _iRightPad = value; }
        }

        public int BottomPad
        {
            get { return _iBottomPad; }
            set { _iBottomPad = value; }
        }

        public int XInterval
        {
            get { return XScale * ChartConstants.MIN_X_INTERVAL; }
        }

        public int TitleHeight
        {
            get { return _iTitleHeight; }
            set { _iTitleHeight = value; }
        }

        public double XMin
        {
            get { return _dMin[ChartConstants.X]; }
            set { _dMin[ChartConstants.X] = value; }
        }

        public double XMax
        {
            get { return _dMax[ChartConstants.X]; }
            set { _dMax[ChartConstants.X] = value; }
        }

        public double YMin
        {
            get { return _dMin[ChartConstants.Y]; }
            set { _dMin[ChartConstants.Y] = value; }
        }

        public double YMax
        {
            get { return _dMax[ChartConstants.Y]; }
            set { _dMax[ChartConstants.Y] = value; }
        }

        public double X2Min
        {
            get { return _dMin[ChartConstants.X2]; }
            set { _dMin[ChartConstants.X2] = value; }
        }

        public double X2Max
        {
            get { return _dMax[ChartConstants.X2]; }
            set { _dMax[ChartConstants.X2] = value; }
        }

        public double Y2Min
        {
            get { return _dMin[ChartConstants.Y2]; }
            set { _dMin[ChartConstants.Y2] = value; }
        }

        public double Y2Max
        {
            get { return _dMax[ChartConstants.Y2]; }
            set { _dMax[ChartConstants.Y2] = value; }
        }

        public Color BorderColor
        {
            get { return _cBorderColor; }
            set { _cBorderColor = value; }
        }

        public Color AxisLineColor
        {
            get { return _cAxisLineColor; }
            set { _cAxisLineColor = value; }
        }

        public bool ShowXAxis
        {
            get { return _isShowXAxis; }
            set { _isShowXAxis = value; }
        }

        public bool ShowYAxis
        {
            get { return _isShowYAxis; }
            set { _isShowYAxis = value; }
        }

        public bool IsShowDayLine
        {
            get { return _isShowDayLine; }
            set { _isShowDayLine = value; }
        }

        protected virtual double CalculateInterval(double dMax, double dMin, double dCount)
        {
            double dInterval = 0;
            int iLength;

            dInterval = (dMax - dMin) / dCount;
            iLength = ((int)Math.Floor(dInterval)).ToString().Length;

            if (dInterval > 1)
            {
                dInterval = Math.Ceiling(dInterval / Math.Pow(10, iLength - 1)) * Math.Pow(10, iLength - 1);
            }
            else
            {
            }

            return dInterval;
        }

        public virtual int[] ValueToPixel(double dX, double dY)
        {
            return ValueToPixel(XDataType == ChartConstants.DATA_TYPE_TIME ? ChartConstants.X : ChartConstants.X2, ChartConstants.Y, dX, dY);
        }

        public virtual int[] ValueToPixel(int iXAxis, int iYAxis, double dX, double dY)
        {
            int[] iReturn = new int[2];
            int iDataLen = EndIndex - StartIndex + 1;

            switch (iXAxis)
            {
                case ChartConstants.X:
                    iReturn[0] = (int)((dX - StartIndex) * XInterval) + (int)_fXOrigin;
                    break;
                case ChartConstants.X2:
                    iReturn[0] = (int)((dX - _dX2MinValue) / _dX2PixelValue) + (int)_fXOrigin;
                    break;
            }

            switch (iYAxis)
            {
                case ChartConstants.Y:
                    iReturn[1] = DrawHeight - (int)((dY - _dMinValue) / _dPixelValue);
                    break;
                case ChartConstants.Y2:

                    break;
            }

            if ((iReturn[0] < _fXOrigin) || (iReturn[0] > _fXOrigin + DrawWidth))
                iReturn[0] = int.MaxValue;

            if ((iReturn[1] < 0) || (iReturn[1] > DrawHeight))
                iReturn[1] = int.MaxValue;

            return iReturn;
        }

        public virtual double[] PixelToValue(int iX, int iY)
        {
            return PixelToValue(XDataType == ChartConstants.DATA_TYPE_TIME ? ChartConstants.X : ChartConstants.X2, ChartConstants.Y, iX, iY);
        }

        public virtual double[] PixelToValue(int iXAxis, int iYAxis, int iX, int iY)
        {
            double[] dReturn = new double[2];

            switch (iXAxis)
            {
                case ChartConstants.X:
                    dReturn[0] = ((double)(iX - _fXOrigin) / (double)XInterval) + StartIndex;
                    break;
                case ChartConstants.X2:
                    dReturn[0] = (iX - LeftPad - ((_iYAxisPosition == ChartConstants.LEFT) && ShowYAxis ? _iAxisWidth : 0)) * _dX2PixelValue + _dX2MinValue;
                    break;
            }

            switch (iYAxis)
            {
                case ChartConstants.Y:
                    dReturn[1] = (Height - _iBottomPad - iY) * _dPixelValue + _dMinValue;
                    break;
                case ChartConstants.Y2:
                    break;
            }

            return dReturn;
        }


        public int DrawHeight
        {
            get
            {
                return ShowXAxis
                  ? Height - BottomPad - TopPad
                  : Height - TopPad;
            }
        }

        public int DrawWidth
        {
            get { return Width - RightPad - LeftPad - (ShowYAxis ? _iAxisWidth : 0); }
        }

        private void DrawChartTitle(Graphics g)
        {
            try
            {
                g.FillRectangle(new SolidBrush(BackColor), new Rectangle(0, 0, Width, TitleHeight));
                g.DrawString(_lstIndicator.Count == 0
                                ? ""
                                : _lstIndicator[0].BaseStudy,
                             Font, new SolidBrush(AxisLineColor),
                             new Point(LeftPad + 2, TopPad + 2));

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "," + ex.StackTrace);
            }
        }

        protected virtual void DrawChartArea(Graphics g)
        {
            try
            {
                Pen pen = new Pen(new SolidBrush(AxisLineColor));
                Pen penHour = new Pen(new HatchBrush(HatchStyle.DashedHorizontal, _cHourLineColor));
                Pen customPen;
                int iHeight = DrawHeight;
                double dCount = Math.Floor(iHeight / (double)ChartConstants.DEFAULT_INTERVAL);
                //double dInterval = (YMax - YMin) / dCount;                                                
                double dInterval;
                double dCurrent;
                int iPosition;
                int iCount;
                int iXSkipCount;
                string strDateTimeFormat;
                string strDateSeparateFormat;
                string strDrawString;
                int iDataLength;
                int iYAxisStart;
                int iYNumberStart;

                SizeF sDateSize;
                DateTime dtCurrent;
                DateTime dtPrevious;
                BarCompressionEnum eCompression;

                g.Clear(BackColor);
                eCompression = _lstIndicator.Count == 0
                                ? BarCompressionEnum.Min_1
                                : _dsDataSource.GetData(_lstIndicator[0].BaseStudy) == null
                                    ? BarCompressionEnum.Min_1
                                    : _dsDataSource.GetData(_lstIndicator[0].BaseStudy).BarCompression;

                dInterval = CalculateInterval(YMax, YMin, dCount);
                iCount = (int)Math.Max(0, Math.Floor((((dInterval * dCount) + YMin) - YMax) / dInterval / 2) - 1);
                //dCurrent = (Math.Floor(YMin / dInterval) - iCount) * dInterval;
                dCurrent = YMin - iCount * dInterval;
                while (dCurrent > YMin)
                    dCurrent -= dInterval;
                _dMinValue = dCurrent;

                //Y軸一個Pixel代表的數值
                _dPixelValue = dInterval / (double)ChartConstants.DEFAULT_INTERVAL;
                dCurrent += dInterval;

                //X2軸一個Pixel代表的數值
                _dX2PixelValue = (X2Max - X2Min) / DrawWidth;
                _dX2MinValue = X2Min;

                //刻度的起始X座標
                iYAxisStart = (_iYAxisPosition == ChartConstants.RIGHT) ? Width - RightPad - (ShowYAxis ? _iAxisWidth : 0) : LeftPad + (ShowYAxis ? _iAxisWidth - 3 : 0);
                //標示數字的起始X座標
                iYNumberStart = (_iYAxisPosition == ChartConstants.RIGHT) ? iYAxisStart + 5 : 2;

                //若顯示X軸, 畫出X軸
                if (ShowXAxis)
                {
                    switch (eCompression)
                    {
                        case BarCompressionEnum.Tick:
                            strDateTimeFormat = "mm:ss";
                            strDateSeparateFormat = "HH:mm";
                            break;
                        case BarCompressionEnum.Daily:
                            strDateTimeFormat = "MM/dd";
                            strDateSeparateFormat = "yy/MM";
                            break;
                        default:
                            strDateTimeFormat = "HH:mm";
                            strDateSeparateFormat = "MM/dd";
                            break;
                    }

                    g.DrawLine(pen, 0, _fYOrigin, Width, _fYOrigin);

                    //計算時間字串的寬度
                    sDateSize = _lstIndicator.Count == 0
                                    ? new SizeF(10, 10)
                                    : _dsDataSource.GetData(_lstIndicator[0].BaseStudy) == null
                                        ? new SizeF(10, 10)
                                        : g.MeasureString(_dsDataSource.GetData(_lstIndicator[0].BaseStudy).DateTime().ToString(strDateTimeFormat), Font);

                    //計算每個時間寬度所要跳過的K棒數
                    iXSkipCount = (int)Math.Ceiling(sDateSize.Width / XInterval);

                    //計算X軸的刻度個數
                    dCount = DrawWidth / (XInterval * iXSkipCount);
                    iDataLength = EndIndex - StartIndex + 1;

                    //畫出X軸刻度
                    switch (XDataType)
                    {
                        case ChartConstants.DATA_TYPE_TIME:
                            for (int i = 0; i < dCount; i++)
                            {
                                dtCurrent = _lstIndicator.Count == 0
                                                ? DateTime.Now
                                                : _dsDataSource.GetData(_lstIndicator[0].BaseStudy) == null
                                                    ? DateTime.Now
                                                    : _dsDataSource.GetData(_lstIndicator[0].BaseStudy).DateTime(i * iXSkipCount + StartIndex);

                                dtPrevious = _lstIndicator.Count == 0
                                                    ? DateTime.Now
                                                    : _dsDataSource.GetData(_lstIndicator[0].BaseStudy) == null
                                                        ? DateTime.Now
                                                        : (i - 1) * iXSkipCount + StartIndex < 0
                                                            ? DateTime.Now
                                                            : _dsDataSource.GetData(_lstIndicator[0].BaseStudy).DateTime((i - 1) * iXSkipCount + StartIndex);
                                g.DrawLine(pen,
                                           new Point((int)_fXOrigin + iXSkipCount * i * XInterval, iHeight),
                                           new Point((int)_fXOrigin + iXSkipCount * i * XInterval, iHeight + 5));

                                if (dtCurrent == DateTime.MinValue)
                                    continue;
                                strDrawString = (dtCurrent.Day != dtPrevious.Day)
                                                    ? dtCurrent.ToString(strDateSeparateFormat)
                                                    : dtCurrent.ToString(strDateTimeFormat);

                                g.DrawString(strDrawString, Font, new SolidBrush(AxisLineColor),
                                             new Point((int)_fXOrigin + iXSkipCount * i * XInterval, iHeight + 5));
                            }
                            break;
                        case ChartConstants.DATA_TYPE_VALUE:
                            for (int i = 0; i < dCount; i++)
                            {
                                g.DrawLine(pen,
                                           new Point((int)_fXOrigin + iXSkipCount * i * XInterval, iHeight),
                                           new Point((int)_fXOrigin + iXSkipCount * i * XInterval, iHeight + 5));
                                strDrawString = PixelToValue((int)(_fXOrigin + iXSkipCount * i * XInterval), iHeight + 5)[0].ToString("0");
                                g.DrawString(strDrawString, Font, new SolidBrush(AxisLineColor),
                                             new Point((int)_fXOrigin + iXSkipCount * i * XInterval, iHeight + 5));

                            }
                            break;
                    }
                }

                //畫出Y軸
                g.DrawLine(pen, _fXOrigin, 0, _fXOrigin, _fYOrigin);

                if (ShowYAxis)
                {
                    for (iPosition = ChartConstants.DEFAULT_INTERVAL; iPosition < iHeight; dCurrent += dInterval, iPosition += ChartConstants.DEFAULT_INTERVAL)
                    {
                        g.DrawLine(pen, new Point(iYAxisStart, iHeight - iPosition), new Point(iYAxisStart + 3, iHeight - iPosition));
                        g.DrawString(dCurrent.ToString("0.00"), Font, new SolidBrush(AxisLineColor), new Point(iYNumberStart, iHeight - iPosition - (int)(Font.Height / 2)));
                    }
                }

                //顯示小時線
                if (_isShowHourLine && XDataType == ChartConstants.DATA_TYPE_TIME)
                {

                    for (int i = StartIndex; i < EndIndex; i++)
                    {
                        dtCurrent = _lstIndicator.Count == 0
                                        ? DateTime.MinValue
                                        : _dsDataSource.GetData(_lstIndicator[0].BaseStudy) == null
                                            ? DateTime.MinValue
                                            : _dsDataSource.GetData(_lstIndicator[0].BaseStudy).DateTime(i);

                        if (dtCurrent.Minute == 0)
                            g.DrawLine(penHour,
                                       new Point((int)_fXOrigin + XInterval * (i - StartIndex), TopPad),
                                       new Point((int)_fXOrigin + XInterval * (i - StartIndex), (int)_fYOrigin));
                    }
                }




                /*
                                iDataLength = _lstIndicator.Count == 0
                                                    ? 0
                                                    : _dsDataSource.GetLength(_lstIndicator[0].BaseStudy);

                                eCompression = _lstIndicator.Count == 0
                                                ? BarCompressionEnum.Min_1 
                                                : _dsDataSource.GetData(_lstIndicator[0].BaseStudy) == null
                                                    ? BarCompressionEnum.Min_1 
                                                    : _dsDataSource.GetData(_lstIndicator[0].BaseStudy).BarCompression;
                
                                g.FillRectangle(new SolidBrush(BackColor), new Rectangle(0, 0, Width, Height));

                                g.DrawString(_lstIndicator.Count == 0
                                                ? ""
                                                : _lstIndicator[0].BaseStudy,
                                             Font, new SolidBrush(AxisLineColor),
                                             new Point(LeftPad + 2, TopPad + 2));
                                // Calculate Interval
                                dInterval = CalculateInterval(YMax, YMin, dCount);
                                iCount = (int)Math.Floor((((dInterval * dCount) + YMin) - YMax) / dInterval / 2) - 1;
                                dCurrent = (Math.Floor(YMin / dInterval) - iCount) * dInterval;
                                dCurrent = (dCurrent > YMin) ? dCurrent - dInterval : dCurrent;
                                _dMinValue = dCurrent;
                                _dPixelValue = dInterval / (double)DEFAULT_INTERVAL;

                                // Draw Axis Line
                                if (ShowXAxis)
                                    g.DrawLine(pen, new Point(0, Height - _iBottomPad), new Point(Width, Height - _iBottomPad));
                                g.DrawLine(pen, new Point(Width - _iRightPad, 0), new Point(Width - _iRightPad, iHeight));
                                dCurrent += dInterval;
                                for (iPosition = DEFAULT_INTERVAL; iPosition < iHeight; dCurrent += dInterval, iPosition += DEFAULT_INTERVAL)
                                {
                                    g.DrawLine(pen, new Point(Width - _iRightPad, iHeight - iPosition), new Point(Width - _iRightPad + 3, iHeight - iPosition));                    
                                    g.DrawString(dCurrent.ToString("0.00"), Font, new SolidBrush(AxisLineColor), new Point(Width - _iRightPad + 5, iHeight - iPosition - (int)(Font.Height / 2)));
                                }

                                switch (eCompression)
                                {
                                    case BarCompressionEnum.Tick:
                                        strDateTimeFormat = "MM/dd HH:mm:dd";
                                        break;
                                    case BarCompressionEnum.Daily:
                                        strDateTimeFormat = "MM/dd";
                                        break;
                                    default:
                                        strDateTimeFormat = "HH:mm";
                                        break;
                                }

                                if (_isShowHourLine)
                                {

                                    for (int i = 0; i < iDataLength; i++)
                                    {
                                        dtCurrent = _lstIndicator.Count == 0
                                                        ?   DateTime.MinValue
                                                        :   _dsDataSource.GetData(_lstIndicator[0].BaseStudy) == null
                                                            ?   DateTime.MinValue
                                                            :   _dsDataSource.GetData(_lstIndicator[0].BaseStudy).DateTime(i);

                                        if (_isShowHourLine & (dtCurrent.Minute == 0))
                                            g.DrawLine(penHour,
                                                       new Point(XInterval * (iDataLength - i - 1), TopPad),
                                                       new Point(XInterval * (iDataLength - i - 1), TopPad + iHeight));
                                    }
                                }

                                foreach (ConstantLine customLine in _lstCustomLine)
                                {
                                    g.DrawLine(new Pen(new SolidBrush(customLine.Color)),
                                               new Point(LeftPad, ValueToPixel(0, customLine.Value)[1]),
                                               new Point(LeftPad + DrawWidth, ValueToPixel(0, customLine.Value)[1]));
                                }

                                if (ShowXAxis == false)
                                    return;

                                sDateSize = _lstIndicator.Count == 0 
                                                ? new SizeF(10, 10)
                                                : _dsDataSource.GetData(_lstIndicator[0].BaseStudy) == null
                                                    ? new SizeF(10, 10)
                                                    : g.MeasureString(_dsDataSource.GetData(_lstIndicator[0].BaseStudy).DateTime().ToString(strDateTimeFormat), Font);

                                iXSkipCount = (int)Math.Ceiling(sDateSize.Width / XInterval);
                                dCount = DrawWidth / (XInterval * iXSkipCount);
                

                                for (int i = 0; (i < dCount) && (i * iXSkipCount < iDataLength); i++)
                                {
                                    dtCurrent = _lstIndicator.Count == 0 
                                                    ? DateTime.Now
                                                    : _dsDataSource.GetData(_lstIndicator[0].BaseStudy) == null
                                                        ? DateTime.Now
                                                        : _dsDataSource.GetData(_lstIndicator[0].BaseStudy).DateTime(iDataLength - i * iXSkipCount - 1);
                                    g.DrawLine(pen,
                                               new Point(iXSkipCount * i * XInterval, iHeight),
                                               new Point(iXSkipCount * i * XInterval, iHeight + 5));
                    
                                    if(dtCurrent == DateTime.MinValue)
                                        continue;
                                    strDrawString = dtCurrent.ToString(strDateTimeFormat);
                                    g.DrawString(strDrawString, Font, new SolidBrush(AxisLineColor),
                                                 new Point(iXSkipCount * i * XInterval, iHeight + 5));
                                }
                */
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "," + ex.StackTrace);
            }
        }

        public void SetDataSource(IDataSource dsDataSource)
        {
            _dsDataSource = dsDataSource;
        }

        public void AddIndicator(IIndicator indicator)
        {
            _lstIndicator.Add(indicator);
            indicator.StartIndex = StartIndex;
            indicator.EndIndex = EndIndex;
            indicator.DataSource = _dsDataSource;
            indicator.ChartMeta = this;
            indicator.Calculate();
        }

        public IIndicator GetIndicatior(int iIndex)
        {
            return (iIndex >= 0) || (iIndex < _lstIndicator.Count)
                        ? _lstIndicator[iIndex]
                        : null;
        }

        public List<IIndicator> GetIndicators()
        {
            return _lstIndicator;
        }

        private Matrix CreateTransform(int iXAxis, int iYAxis)
        {
            Matrix m = null;

            try
            {
                float fXScale = (XDataType == ChartConstants.DATA_TYPE_TIME)
                                                ? XScale
                                                : (float)(DrawWidth / (_dMax[iXAxis] - _dMin[iXAxis] + 1));

                float fYScale = -(float)(DrawHeight / (_dMax[iYAxis] - _dMin[iYAxis]));

                float fDX = (float)_dMin[iXAxis];
                float fDY = (float)_dMin[iYAxis] * (-fYScale);

                m = new Matrix(fXScale, 0, 0, fYScale, fDX + _fXOrigin, fDY + Height);
            }
            catch { }

            return m;
        }

        public void CalculateAxisRange()
        {
            double dEdge = 0;
            switch (_eXScaleType)
            {
                case ScaleTypeEnum.Screen:
                    _dMax[ChartConstants.X] = -double.MaxValue;
                    _dMin[ChartConstants.X] = double.MaxValue;
                    _dMax[ChartConstants.X2] = -double.MaxValue;
                    _dMin[ChartConstants.X2] = double.MaxValue;

                    foreach (IIndicator indicator in _lstIndicator)
                    {
                        indicator.StartIndex = StartIndex;
                        indicator.EndIndex = EndIndex;
                        if (indicator.XAxis == ChartConstants.X)
                        {
                            XMax = Math.Max(XMax, indicator.XMax);
                            XMin = Math.Min(XMin, indicator.XMin);
                        }

                        if (indicator.XAxis == ChartConstants.X2)
                        {
                            X2Max = Math.Max(X2Max, indicator.XMax);
                            X2Min = Math.Min(X2Min, indicator.XMin);
                        }
                    }

                    for (int i = 0; i < _dMax.Length; i++)
                    {
                        switch (i)
                        {
                            case ChartConstants.X:
                            case ChartConstants.X2:
                                dEdge = (_dMax[i] - _dMin[i]) * _dXEdgeRatio / 2.0;
                                _dMin[i] -= dEdge;
                                _dMax[i] += dEdge;
                                break;
                        }
                    }
                    break;
                case ScaleTypeEnum.EntireData:
                    break;
                case ScaleTypeEnum.UserDefine:
                    break;
            }

            switch (_eYScaleType)
            {
                case ScaleTypeEnum.Screen:
                    _dMax[ChartConstants.Y] = -double.MaxValue;
                    _dMin[ChartConstants.Y] = double.MaxValue;
                    _dMax[ChartConstants.Y2] = -double.MaxValue;
                    _dMin[ChartConstants.Y2] = double.MaxValue;

                    foreach (IIndicator indicator in _lstIndicator)
                    {
                        indicator.StartIndex = StartIndex;
                        indicator.EndIndex = EndIndex;
                        if (indicator.YAxis == ChartConstants.Y)
                        {
                            YMax = Math.Max(YMax, indicator.YMax);
                            YMin = Math.Min(YMin, indicator.YMin);
                        }

                        if (indicator.YAxis == ChartConstants.Y2)
                        {
                            Y2Max = Math.Max(Y2Max, indicator.YMax);
                            Y2Min = Math.Min(Y2Min, indicator.YMin);
                        }
                    }

                    for (int i = 0; i < _dMax.Length; i++)
                    {
                        switch (i)
                        {
                            case ChartConstants.Y:
                            case ChartConstants.Y2:
                                dEdge = (_dMax[i] - _dMin[i]) * _dYEdgeRatio / 2.0;
                                _dMin[i] -= dEdge;
                                _dMax[i] += dEdge;
                                break;
                        }
                    }
                    break;
                case ScaleTypeEnum.EntireData:
                    break;
                case ScaleTypeEnum.UserDefine:
                    break;
            }

        }

        public virtual void PaintChart()
        {
            try
            {
                CalculateAxisRange();
                CalculateOrigin();
                Graphics g;
                Graphics eg = CreateGraphics();

                if (Visible == false)
                    return;

                Bitmap bitmap = new Bitmap(Width, Height);
                g = Graphics.FromImage(bitmap);

                DrawChartArea(g);

                foreach (IIndicator indicator in _lstIndicator)
                {
                    indicator.Calculate();
                    indicator.Draw(g);
                }

                eg.DrawImage(bitmap, 0, 0);
                eg.Dispose();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "," + ex.StackTrace);
            }
            finally
            {
                _lLastUpdateTicks = DateTime.Now.Ticks;
            }
        }

        private PointData CreatePointData(MouseEventArgs e)
        {
            double[] dValues;
            PointData pointData = new PointData();

            pointData.ChartName = ChartName;
            pointData.XPixel = e.X;
            pointData.YPixel = e.Y;
            dValues = PixelToValue(e.X, e.Y);
            pointData.XValue = (float)dValues[0];
            pointData.YValue = (float)dValues[1];

            return pointData;
        }

        private void ChartBase_MouseDown(object sender, MouseEventArgs e)
        {            
            if (OnMouseDown != null)
            {                
                OnMouseDown(CreatePointData(e));
            }
        }

        private void ChartBase_MouseEnter(object sender, EventArgs e)
        {
            
        }

        private void ChartBase_MouseLeave(object sender, EventArgs e)
        {

        }

        private void ChartBase_MouseMove(object sender, MouseEventArgs e)
        {
            if (OnMouseMove != null)
            {
                OnMouseMove(CreatePointData(e));
            }
        }

        private void ChartBase_MouseUp(object sender, MouseEventArgs e)
        {
            if (OnMouseUp != null)
            {
                OnMouseUp(CreatePointData(e));
            }
        }

        private void ChartBase_MouseClick(object sender, MouseEventArgs e)
        {
            if (OnMouseClick != null)
            {
                OnMouseClick(CreatePointData(e));
            }
        }

        private void ChartBase_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (OnMouseDoubleClick != null)
            {
                OnMouseDoubleClick(CreatePointData(e));
            }
        }

    }
}
